// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	2/16/2015 8:05:31 PM				
// -----------------------------

#include "CGraphicsDevice.h"

#include "IWindow.h"

namespace Flow
{

	CGraphicsDevice::CGraphicsDevice()
		: m_lpDevice(nullptr)
		, m_lpDeviceContext(nullptr)
		, m_lpSwapChain(nullptr)
		, m_lpBackBufferView(nullptr)
		, m_lpDepthStencil(nullptr)
		, m_lpDepthStencilView(nullptr)
	{
	}

	CGraphicsDevice::~CGraphicsDevice()
	{
		SafeReleaseInterface(&m_lpDepthStencilView);
		SafeReleaseInterface(&m_lpDepthStencil);
		SafeReleaseInterface(&m_lpBackBufferView);

		SafeReleaseInterface(&m_lpSwapChain);
		SafeReleaseInterface(&m_lpDeviceContext);
		SafeReleaseInterface(&m_lpDevice);
	}

	void CGraphicsDevice::Release()
	{
		delete this;
	}

	Bool CGraphicsDevice::Initialize(IWindow* lpWindow)
	{
		// Device Create Flags
		UInt32 uCreateFlags = 0;
#ifdef _DEBUG
		uCreateFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

		// Create the D3D11 Device
		D3D_FEATURE_LEVEL arrFeatureLevels[] =
		{
			D3D_FEATURE_LEVEL_9_3,
		};
		UInt32 uFeatureLevelCount = ARRAYSIZE(arrFeatureLevels);
		HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, uCreateFlags,
			arrFeatureLevels, uFeatureLevelCount, D3D11_SDK_VERSION,
			&m_lpDevice, NULL, &m_lpDeviceContext);
		if (FAILED(hr))
			return false;

		return InitializeView(lpWindow);
	}
	Bool CGraphicsDevice::InitializeView(IWindow* lpWindow)
	{
		// Create the DXGI Factory
		IDXGIFactory* lpFactory = nullptr;
		HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory*), reinterpret_cast<void**>(&lpFactory));
		if (FAILED(hr))
			return false;

		// Setup the Swap Chain Descriptor
		DXGI_SWAP_CHAIN_DESC swapDesc = {};
		swapDesc.BufferCount = 1;
		swapDesc.BufferDesc.Width = lpWindow->GetWidth();
		swapDesc.BufferDesc.Height = lpWindow->GetHeight();
		swapDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		swapDesc.BufferDesc.RefreshRate.Numerator = 60;
		swapDesc.BufferDesc.RefreshRate.Denominator = 1;
		swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapDesc.OutputWindow = lpWindow->GetHandle();
		swapDesc.SampleDesc.Count = 1;
		swapDesc.SampleDesc.Quality = 0;
		swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		swapDesc.Windowed = TRUE;

		// Create the Swap Chain
		hr = lpFactory->CreateSwapChain(m_lpDevice, &swapDesc, &m_lpSwapChain);
		if (FAILED(hr))
			return false;

		// Get the Backbuffer texture
		ID3D11Texture2D* lpTexture = nullptr;
		hr = m_lpSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&lpTexture);
		if (FAILED(hr))
			return false;

		// Create the backbuffer view
		hr = m_lpDevice->CreateRenderTargetView(lpTexture, NULL, &m_lpBackBufferView);
		if (FAILED(hr))
			return false;

		// Query the Backbuffer Dimensions
		D3D11_TEXTURE2D_DESC backBufferDesc = {};
		lpTexture->GetDesc(&backBufferDesc);

		// Create the Depth Stencil Texture
		D3D11_TEXTURE2D_DESC depthDesc = {};
		depthDesc.ArraySize = 1;
		depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
		depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		depthDesc.MipLevels = 1;
		depthDesc.SampleDesc.Count = 1;
		depthDesc.SampleDesc.Quality;
		depthDesc.Usage = D3D11_USAGE_DEFAULT;
		depthDesc.Width = backBufferDesc.Width;
		depthDesc.Height = backBufferDesc.Height;

		hr = m_lpDevice->CreateTexture2D(&depthDesc, NULL, &m_lpDepthStencil);
		if (FAILED(hr))
			return false;

		// Create the Depth Stencil View
		D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc = {};
		depthViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;

		hr = m_lpDevice->CreateDepthStencilView(m_lpDepthStencil, &depthViewDesc, &m_lpDepthStencilView);
		if (FAILED(hr))
			return false;

		SafeReleaseInterface(&lpTexture);
		SafeReleaseInterface(&lpFactory);

		return true;
	}

	void CGraphicsDevice::Begin(Float r, Float g, Float b) const
	{
		Float rgba[] = { r, g, b, 1.0f };
		m_lpDeviceContext->ClearDepthStencilView(m_lpDepthStencilView, 0, 1.0f, 0x00);
		m_lpDeviceContext->ClearRenderTargetView(m_lpBackBufferView, rgba);

		ID3D11RenderTargetView* renderTargets[] = { m_lpBackBufferView };
		m_lpDeviceContext->OMSetRenderTargets(1, renderTargets, m_lpDepthStencilView);
	}
	void CGraphicsDevice::End() const
	{
		m_lpSwapChain->Present(1, 0);
	}
}